/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  numDefine100,
  numDefine2,
  numDefine3,
  numDefine16,
  numDefine5,
  numDefine7,
  numDefine8,
  numDefineFloatLog346,
  numDefine20,
  numDefine4,
  numDefine24,
  numDefine256,
  numDefine1024,
  numDefine300,
  numDefine512,
  numDefine404,
  numDefine800,
  numDefine0xffffff,
  numDefine01026,
  numDefine0xff,
  numDefine0xfffffff8,
  numDefine640,
  numDefine480,
  numDefine8192,
  numDefine768,
  numDefineFloat100,
} from './mandreel-part1/Class/DevNumber';

import { deBugLog } from './commonFile';
import {
  tlsfPtr,
  mallocInt,
  assert,
  mandreelTotalMemory,
  alert,
  puts,
  getStringFromPtr,
  functionTable,
  llvm2EGlobalCtors,
  GetItemArr,
  MandreelCanvas,
  MandreelCanvasDiv,
  free,
  dump,
  fillPtrFromString,
  gStackPointer,
  dateNow,
  updateMandreelStats,
  mandreelCallConstructors,
  resize,
  iMandreelTextureAsyncGetPackOffset,
  mandreelInternalSetResolution,
  mandreelOpenls,
  llvmReadDouble,
  tlsfRealloc,
  myHeapNewPos,
  mandreelCacheFiles,
  sufix,
  imandreelGl,
  fileIds,
  arrayIdsOgl,
  realloc,
  mandreelGetlocalstorage,
  CMalloc
} from './commonFile';
import { commonVariable, heapClassInst, nullMandreelAudio } from './mandreel-part1/Class/CommonClassFile';
import { ImandreelCtxCanvas } from './mandreel-part1/Class/ImandreelCtxCanvas';
import { mandreelDocument, MandreelDocument } from './mandreel-part1/Class/MandreelDocument';
import { lzMA } from './mandreel-part1/Class/LzmaClass';
import { LzmaClass } from './mandreel-part1/Class/LzmaClass';
import { swfObject, Param } from './mandreel-part1/Class/SwfObject';
import { MandreelWindow, mandreelWindow } from './mandreel-part1/Class/MandreelWindow';
import { MouseEvent } from './mandreel-part1/Class/MouseEvent';
import { initNew, navigator, GmandreelFs, variableSet, WebkitAudioContext } from './mandreelFile1';
import { mandreelStartAudio, waInitWebAudio, mandreelWebAudioPreloadAssets, draw } from './mandreelFile1';
import { mandreelInternalPreupdate, mandreelInternalUpdate, mouseWheelDelta, mouseMove, mouseButton, mouseDoubleClick, keyEvent } from './mandreelFile7';
import { mandreelInternalInit } from './mandreelFile7';
import { globalInit } from './mandreelFile8';
import { globalInit1 } from './mandreelFile9';
import { globalInit2 } from './mandreelFile10';
import { globalInit3 } from './mandreelFile11';
import { globalInit4 } from './mandreelFile12';
import { globalInit5 } from './mandreelFile13';
import { globalInit6 } from './mandreelFile14';
import { globalInit7, registerDelegateInitialize, swriteIndexInitialize } from './mandreelFile15';

class HeapValue {
  l: number = 0;
  h: number = 0;
}

class MandreelEmbedPlugin {
  mandreelInterCalls(str1: string, ptr: number = 0, value: number = 0): void {}
}

class Navigatortype {
  appName: string | null = '';
  userAgent: string | null = '';
}

let gMandreelSwf: Param | null = null;

class MandInt {
  mandreelInterGetSWF(): Param {
    if (gMandreelSwf !== null) {
      return gMandreelSwf!;
    }
    gMandreelSwf = swfObject.getObjectById(mandreelAppCanvasDiv);
    return gMandreelSwf;
  }
}

class GetSwf extends MandInt {
  constructor() {
    super();
    this.mandreelInterGetSWF();
  }

  mandreelInterGetGlobalStack(): number {
    return 1;
  }

  mandreelInterGetGlobalStack2(): number {
    return 1;
  }

  mandreelInterCallFunctionRaw(x: number, y: string, z: string): string {
    return ' ';
  }
}

class MandreelHttpRequest {
  url: string = '';
  response: string | null = null;
  readyState: number | null = null;
  status: number | null = null;
  onreadystatechange: () => void = () => {};

  constructor(url: string) {
    this.url = url;
  }

  open(type: string, url: string, someBool: boolean): void {
    this.url = url;
  }

  overrideMimeType(): void {}

  send(): void {
    this.response = null;
    this.readyState = numDefine4;
    this.status = 0;
  }
}

class Resxy {
  x: number = 0;
  y: number = 0;
}

class FunClass {
  funcName: string = '';
  param: string = '';

  constructor(funcName: string, param: string) {
    this.funcName = funcName;
    this.param = param;
  }
}

class ReadWrite {
  readWrite: string = '';
}

class Mandreelindexed {
  db: DbClass | null = new DbClass();
  callbackInit: boolean;

  constructor(db: DbClass | null, callbackInit: boolean) {
    this.db = db;
    this.callbackInit = callbackInit;
  }

  onerror(e: string): void {
    dump(e);
  }
}

class DbClass {
  transaction(cache: Array<string>, type: string): Dbtrans {
    return new Dbtrans();
  }
}

class Dbtrans {
  objectStore(cache: string): DbRequest {
    return new DbRequest();
  }
}

class DbRequest {
  put(param: string): Request {
    return new Request();
  }

  get(key: string): Request {
    return new Request();
  }
}

class Request {
  onsuccess(e: string): void {
    dump('mandreelIndexedDB.save ok ');
  }

  onerror(e: string, fileName: string): void {
    dump('mandreelIndexedDB.save onerror ' + fileName);
    dump(e);
  }

  createObjectStore(name: string, type: string): void {}
}

class GetTime {
  getTime(): void {}
}

let mandreelEmbedPlugin: MandreelEmbedPlugin = new MandreelEmbedPlugin();
let mandreelPluginCurrentSp: number = 0;
let mandreelHttpRequestModeGet: number = 0;
let mandreelHttpRequestModePost: number = 1;
let mandreelHttpRequestModePut: number = numDefine2;
let mandreelHttpRequestStatusError: number = 0;
let mandreelHttpRequestStatusBusy: number = 1;
let mandreelHttpRequestStatusFinished: number = numDefine2;
let mandreelHttpRequestStatusInit: number = numDefine3;
let mandeelLog10: number = numDefineFloatLog346;
let mandreelJsMappingIdsFree: Array<number> = new Array<number>();
let myOldConstructors: (x: number, y: number, z: number) => void;
let mandreelAppStartStateFunc: (state: string, param: string | number, mandreelAppCanvasHeight?: number) => void;
let mandreelAppLog: boolean = false;
let mandreelAppWorkingFolder: string = 'data/js/';
let mandreelAppWidthSrc: number = numDefine1024;
let mandreelAppHeightSrc: number = numDefine768;
let mandreelAppReadDataFromLocalHost: boolean = false;
let mandreelAppMandreelJs: string = 'mandreel.js';
let mandreelAppMandreelJsCompressed: boolean = false;
let mandreelAppLocalHost: string = 'http://localhost';
let mandreelAppReadMandreelJsFromLocalHost: boolean = false;
let mandreelAppHostedAudioServer: string | null = null;
let mandreelAppHostedAudioUrl: string | null = null;
let mandrelCurrentFatVersion: string = '1.4';
let mandreelAppCanvasWidth: number = numDefine1024;
let mandreelAppCanvasHeight: number = numDefine768;
let mandreelAppCanvasName: string = 'canvas';
let mandreelAppCanvasDiv: string = 'canvasDiv';
let mandreelAppUseFlashSockets: boolean = false;
let mandreelAppUsePackAsyncTexture: Array<string> = new Array<string>();
let mandreelAppForceFocus: boolean = true;
let imandreelPauseGame: boolean = false;
let mandreelFatPreloadRequest: MandreelHttpRequest | null = null;
let mandreelPackfiledataName: string | null = null;
let mandreelPackfiledataCompressed: boolean = false;
let mandreelPackfiledataSize: number = 0;
let mandreelTotalPacktextureSize: number = 0;
let mandreelLoadedPacktextureSize: number = 0;
let mandreelJslzmaSize: number = 0;
let mandreelSwfSize: number = 0;
let timeouts: Array<string | (() => void)> = new Array<string | (() => void)>();
let imandreelIsReady: boolean = false;
let mandreeltimeouts: Array<number> = new Array<number>();
let mandreelJsScriptLoadedLoaded: boolean = false;
let mandreelSwfLastTotalSize: number = 0;
let mandreelCurrentPackTexture: string = '';
let mandreelUpdateTotalSize: number = 0;
let isIEOld: string = '';
let isFireFoxNew: string = '';
let isChrome: string = '';
let imandreelOldTime: number = 0;
let imandreelLastMovex: number = 0.0;
let imandreelLastMovey: number = 0.0;
let mandreelMouseDown: boolean = false;
let mandreelRmouseDown: boolean = false;
let imandreelCtxCanvas: ImandreelCtxCanvas | null = new ImandreelCtxCanvas();
let maxOglId: number = numDefine8192;
let arrayIdsOglEnable: Array<number> = new Array<number>();
let gCurrentProgramId: number = 0;
let uniformArrays2: Array<Float32Array> = new Array<Float32Array>();
let uniformArrays3: Array<Float32Array> = new Array<Float32Array>();
let uniformArrays4: Array<Float32Array> = new Array<Float32Array>();
let uniformArraysCreated: number = 0;
let mandreelDrawEnable: boolean = true;
let mySuperId: number = 1;
let imandreelFlashGlobalSp: number = 0;
let imandreelFlashGlobalSp2: number = 0;
let imandreelFlashCurrentSp: number = 0;

let indexedDB = mandreelWindow.indexedDB !== 0 || mandreelWindow.webkitIndexedDB !== 0 || mandreelWindow.mozIndexedDB !== 0;

let mandreelInterGetSWF: GetSwf = new GetSwf();
let iDBTransactions = new ReadWrite();
let mandreelcalljsTypeReturnVoid: number = 0;
let mandreelcalljsTypeInt: number = 1;

let mandreelcalljsTypeFloat: number = numDefine2;
let mandreelcalljsTypeString: number = numDefine3;
let mandreelcalljsTypeReturnInt: number = numDefine4;
let mandreelcalljsTypeReturnFloat: number = numDefine5;
let mandreelTextureAsyncTextures: number = 0;
let mandreelTextureAsyncTexturesLoaded: number = 0;
let glBufferDataArray: Array<number> = new Array<number>();
let mandreelAsyncCallsMandreel: Array<FunClass> = new Array<FunClass>();
let mandreelAsyncCallsJs: Array<FunClass> = new Array<FunClass>();
let gMandreelCacheFiles: boolean = false;
let gMandreelFrameLocked: boolean = false;
let gMandreelFrameInframe: boolean = false;
let mandreelIndexedDB: Mandreelindexed = new Mandreelindexed(null, true);

function mandreelLoadFat(): void {
  let workingFolder = mandreelAppWorkingFolder;
  if (mandreelAppReadDataFromLocalHost) {
    workingFolder = mandreelAppLocalHost + '/' + mandreelAppWorkingFolder;
  }
  let url = workingFolder + 'mandreel.fat.dat';
  mandreelFatPreloadRequest = new MandreelHttpRequest(url);
  if (commonVariable.mandreelAppPlatform === 'nacl') {
    url = workingFolder + 'mandreel.fat';
  }
  mandreelFatPreloadRequest?.open('GET', url, true);
  mandreelFatPreloadRequest.onreadystatechange = (): void => {
    if (mandreelFatPreloadRequest?.readyState !== numDefine4) {
      return;
    }
    if (mandreelFatPreloadRequest?.status !== numDefine404 && mandreelFatPreloadRequest?.response !== null) {
      commonVariable.mandreelFatData = mandreelFatPreloadRequest?.response;
    }
    if (commonVariable.mandreelFatData === '') {
      dump('error loading mandreel.fat file, Maybe the working folder is not correctly set???');
    }

    let packfiledata: string | null = null;
    let compressed = false;
    let packfiledataSize = 0;
    let totalPacktextureSize = 0;
    let pogsize = 0;

    let fatLines = commonVariable.mandreelFatData.split('\n');
    for (let i = 0; i < fatLines.length; ++i) {
      let line = mandreelRemovecr(String(fatLines[i]));
      let params = line.split(',');
      if (params[0] === 'version') {
        if (params[1] !== mandrelCurrentFatVersion) {
          dump('warning: mandreel.fat version number is (' + params[1] + ') and it should be (' + mandrelCurrentFatVersion + ')');
        }
      } else if (
        params[0] === 'platform' &&
        params[1] !== 'js' &&
        (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas')
      ) {
        dump('warning: mandreel.fat platform is (' + params[1] + ') and it should be (js)');
      } else if (params[0] === 'platform' && params[1] !== 'nacl' && commonVariable.mandreelAppPlatform === 'nacl') {
        dump('warning: mandreel.fat platform is (' + params[1] + ') and it should be (nacl)');
      } else if (params[0] === 'packdata') {
        packfiledata = String(params[1]);
      } else if (params[0] === 'flashsockets') {
        mandreelAppUseFlashSockets = true;
      } else if (params[0] === 'packtexture') {
        let filename = params[1];
        mandreelAppUsePackAsyncTexture.push(String(filename));
      } else if (params[0] === 'audiopreloadfile') {
      } else if (params[0] === 'audiodriver' && params[1] === 'webaudio') {
      }
    }
    if (variableSet.preCreatedWebAudioContext !== null) {
      variableSet.mandreelTotalPogfileSize = pogsize;
      mandreelPackfiledataName = packfiledata;
      mandreelPackfiledataCompressed = compressed;
      mandreelPackfiledataSize = packfiledataSize;
      mandreelTotalPacktextureSize = totalPacktextureSize;
      mandreelFatLoaded();
    }
  };
  mandreelFatPreloadRequest?.send();
}

function mandreelAppStart(startStateFunc: () => void, params: Param): void {
  mandreelAppStartStateFunc = startStateFunc;

  if (params.log) {
    mandreelAppLog = params.log;
  }

  if (params.platform.length !== 0) {
    commonVariable.mandreelAppPlatform = params.platform;
  }

  if (params.mandreelJSCompressed) {
    mandreelAppMandreelJsCompressed = params.mandreelJSCompressed;
  }

  if (
    commonVariable.mandreelAppPlatform !== 'webgl' &&
    commonVariable.mandreelAppPlatform !== 'flash' &&
    commonVariable.mandreelAppPlatform !== 'nacl' &&
    commonVariable.mandreelAppPlatform !== 'canvas' &&
    commonVariable.mandreelAppPlatform !== 'plugin'
  ) {
    mandreelAppStartStateFunc('error', 'platform (' + commonVariable.mandreelAppPlatform + ') not supported');
    return;
  }

  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    mandreelAppWorkingFolder = 'data/js/';
    if (params.workingFolderWebgl.length > 0) {
      mandreelAppWorkingFolder = params.workingFolderWebgl;
    }
    if (mandreelWindow.float64Array.length !== 0) {
      mandreelAppStartStateFunc('error', 'Browser unsupported: float64Array not available');
      return;
    }

    let flashElement = mandreelDocument.getElementById('FlashDiv');
    if (flashElement !== null) {
      flashElement.style!.visibility = 'hidden';
      flashElement.style!.width = '0px';
      flashElement.style!.height = '0px';
    }

    if (params.webglCanvas.length <= 0) {
      mandreelAppStartStateFunc('error', 'canvas parameter not found');
      return;
    }
    let canvas = mandreelDocument.getElementById(params.webglCanvas);
    if (canvas === null) {
      mandreelAppStartStateFunc('error', 'canvas object (' + params.webglCanvas + ') not found');
      return;
    }

    if (params.width !== null) {
      canvas!.width = params.width;
      mandreelAppWidthSrc = params.width;
    }

    if (params.height !== null) {
      canvas!.height = params.height;
      mandreelAppHeightSrc = params.height;
    }
    if (commonVariable.mandreelAppPlatform === 'webgl') {
    }

    if (commonVariable.mandreelAppPlatform === 'canvas') {
      imandreelCtxCanvas = canvas!.getContext('2d');
      if (imandreelCtxCanvas === null) {
        mandreelAppStartStateFunc('error', 'canvas context numDefine2d not found');
        return;
      }
    }
    if (params.cache !== null) {
    } else {
      if (mandreelAppStartStateFunc !== null) {
        mandreelAppStartStateFunc('loadingFat', '');
      }
      mandreelLoadFat();
    }
  }
  if (commonVariable.mandreelAppPlatform === 'flash') {
    mandreelAppWorkingFolder = 'data/as3/';
    if (params.workingFolderFlash !== null) {
      mandreelAppWorkingFolder = params.workingFolderFlash;
    }

    if (!swfObject.hasFlashPlayerVersion('11.2.0')) {
      mandreelAppStartStateFunc('error', 'flash numDefine11 not found');
      return;
    }

    if (params.flashCanvas !== null) {
      mandreelAppStartStateFunc('error', 'canvas parameter not found');
      return;
    }

    if (true) {
      let canvas = mandreelDocument.getElementById(mandreelAppCanvasDiv);
      if (canvas !== null) {
        canvas.style!.visibility = 'hidden';
        canvas.style!.width = '0px';
        canvas.style!.height = '0px';
      }
    }

    if (params.width !== null) {
      mandreelAppCanvasWidth = params.width;
    }

    if (params.height !== null) {
      mandreelAppCanvasHeight = params.height;
    }

    mandreelAppCanvasDiv = params.flashCanvas;

    let mandreelSocketsSwf = 'mandreel.swf';
    try {
      if (params.swfFlash !== null) {
        mandreelSocketsSwf = params.swfFlash;
      }

      let myFlashvars = 'workingFolder=';
      if (params.log === true) {
        myFlashvars += '&log=true';
        myFlashvars += '&width=' + String(params.width);
        myFlashvars += '&height=' + String(params.height);
        myFlashvars += '&swfloader=' + mandreelSocketsSwf;
      }
      if (params.restoreContext === true) {
        myFlashvars += '&restoreContext=1';
      }

      if (params.antialiasing !== null) {
        myFlashvars += '&antialiasing=' + String(params.antialiasing);
      }

      if (params.rightClickEnable === true) {
        myFlashvars += '&rightClick=1';
      }

      if (params.disableDepth === true) {
        myFlashvars += '&disableDepth=1';
      }

      let swfname = 'mandreelloader.swf';
      if (params.swfPreloader === false) {
        swfname = mandreelSocketsSwf;
      }
    } catch (err) {
      mandreelAppStartStateFunc('error', 'exception ' + ' while loading ' + mandreelSocketsSwf);
      return;
    }
    appStartState('loadingScript');
  }
  if (commonVariable.mandreelAppPlatform === 'nacl') {
    mandreelAppWorkingFolder = 'data/nacl/';
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc('loadingFat', '');
    }
    mandreelLoadFat();
  }

  if (commonVariable.mandreelAppPlatform === 'plugin') {
    mandreelInitPluginPlatform(params);
  }
}

function startApp(platform: string = ''): void {
  let params: Param = new Param(platform);
  mandreelAppStart(appStartState, params);
}

function mandreelAppStartRenderWebGL(): void {
  mandreelAppInit();
  imandreelIsReady = true;
}

function mandreelAppStartReady(): void {
  if (commonVariable.mandreelAppPlatform === 'nacl') {
    waInitWebAudio();
    mandreelWebAudioPreloadAssets();
  } else {
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartRenderWebGL();
      mandreelAppStartStateFunc!('ready', mandreelAppCanvasWidth, mandreelAppCanvasHeight);
    }
  }
}

function mandreelAppAudioReady(): void {
  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('audioLoaded', '');
  }
  mandreelAppStartReady();
}

function setupMandreel(): void {
  if (commonVariable.heapNewPos === numDefine512) {
    registerDelegateInitialize();
    CMalloc.malloc();
  }
  swriteIndexInitialize();
  myOldConstructors = mandreelCallConstructors;
  startMandreelTimer();
  startApp();
  mandreelAppAudioReady();
}

function runMandreel(): void {
  commonVariable.mandreelCurrentTime = 0;
  let sp: number = gStackPointer + numDefine800 * numDefine1024;
  for (let i = 0; i < mandreelTotalMemory / numDefine4; i++) {
    heapClassInst.heap32[i] = heapClassInst.myHeap32[i];
  }
  tlsfPtr.tlsfPtr = 0;
  commonVariable.heapNewPos = myHeapNewPos;
  myOldConstructors(llvm2EGlobalCtors, numDefine5, sp);
  heapClassInst.heapU32[sp >> numDefine2] = numDefine640;
  heapClassInst.heapU32[(sp + numDefine4) >> numDefine2] = numDefine480;
  mandreelInternalSetResolution(sp);
  mandreelInternalInit(gStackPointer + numDefine800 * numDefine1024);
  initNew(gStackPointer + numDefine800 * numDefine1024);
  for (let i = 0; i < numDefine20; i++) {
    render();
    mandreelFlushTimeouts();
    updateMandreelStats(ArkTools.timeInUs());
  }
  mandreelCheckState();
}

function startMandreelTimer(): void {
  commonVariable.mandreelSampleTimeStart = ArkTools.timeInUs();
}

function latencyMandreel(): Array<number> {
  return commonVariable.mandreelPauseTimes;
}

function mandreelSetTimeout(cmd: string | (() => void), delay?: number, lzma: LzmaClass | null = null, b: boolean = false): void {
  timeouts.push(cmd);
}

function mandreelFlushTimeouts(): void {
  while (mandreeltimeouts.length !== 0) {
    let next = mandreeltimeouts.pop();
  }
}

function mandreelCheckState(): void {
  let sum: number = 0;
  for (let i = 0; i < heapClassInst.heap32.length; i += numDefine100) {
    sum = (sum * numDefine3 + heapClassInst.heap32[i]) & numDefine0xffffff;
  }
  deBugLog(`sum:${sum}`);
  if (sum !== numDefine01026) {
    alert('Check sum mismatch: expected ???, actual ' + String(sum));
  }
}

function appStartState(state: string = '', param: string = ''): void {
  if (state === 'loadingScript') {
  }

  if (state === 'scriptLoaded') {
  }

  if (state === 'loadingData') {
  }

  if (state === 'loadingAudio') {
  }

  if (state === 'audioLoaded') {
  }

  if (state === 'ready') {
    let canvasElement = mandreelDocument.getElementById('loading');
    if (canvasElement !== null && canvasElement?.style !== null) {
      canvasElement!.style.visibility = 'hidden';
    }
  }

  if (state === 'error') {
    alert(param);
  }
}

function mandreelSwfSizeLoaded(str: string): void {
  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('scriptLoaded', '');
  }
}

function mandreelNextDecompress(mandreelResultLzma: LzmaClass): void {
  if (mandreelAppStartStateFunc !== null) {
    let totalBytesLeft = mandreelResultLzma.totalSize - mandreelResultLzma.remainingBytes;
    let percentage: number;
    if (totalBytesLeft === 0) {
      percentage = 0;
    } else {
      percentage = (numDefine100 * totalBytesLeft) / mandreelResultLzma.totalSize;
    }
    mandreelAppStartStateFunc!('uncompressingDataUpdate', percentage);
  }

  let oldResult = mandreelResultLzma;

  lzMA.decompressTwo(mandreelResultLzma.inStream, mandreelResultLzma.inStream, mandreelResultLzma.outStream, mandreelResultLzma);
}
function mandreelJsScriptLoaded(): void {
  if (mandreelJsScriptLoadedLoaded) {
    return;
  }

  if (mandreelCacheFiles.size === 0) {
    mandreelSetTimeout(mandreelJsScriptLoaded, numDefineFloat100);
    return;
  }

  mandreelJsScriptLoadedLoaded = true;

  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('scriptLoaded', '');
  }

  if (mandreelPackfiledataName !== null) {
  } else {
    imandreelJsScriptLoaded();
  }
}

function imandreelLoadAudio(): void {
  commonVariable.gMandreelWorkingFolder = mandreelAppWorkingFolder;
  if (mandreelAppReadDataFromLocalHost) {
    commonVariable.gMandreelWorkingFolder = mandreelAppLocalHost + '/' + mandreelAppWorkingFolder;
  }

  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('loadingAudio', '');
  }
  mandreelStartAudio(mandreelAppHostedAudioServer, mandreelAppHostedAudioUrl);
}

function imandreelUpdateLoad(size: number, total: number): void {
  let total0 = total;
  if (total0 === 0) {
    total0 = mandreelPackfiledataSize + mandreelTotalPacktextureSize + variableSet.mandreelTotalPogfileSize + mandreelJslzmaSize + mandreelSwfSize;
  }

  mandreelUpdateTotalSize += size;

  let percentage = (numDefine100 * mandreelUpdateTotalSize) / total0;

  if (percentage > numDefine100) {
    percentage = numDefine100;
  }
  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('loadingProgress', percentage);
  }
}

function imandreelLoadNextTexturePack(): void {
  if (mandreelAppUsePackAsyncTexture.length !== 0) {
    mandreelCurrentPackTexture !== mandreelAppUsePackAsyncTexture!.pop();
  } else {
    imandreelLoadAudio();
  }
}

function imandreelJsScriptLoaded(): void {
  imandreelLoadNextTexturePack();
}

function mandreelDecompressJSReady(code: string, saveFile: boolean): void {
  if (saveFile) {
    mandreelFsSaveFile(mandreelAppMandreelJs + '.lzma', code);
  }

  let ga = mandreelDocument.createElement('script');
  ga.type = 'text/javascript';
  ga.text = code;

  let s = mandreelDocument.getElementsByTagName('script')[0];
  s.parentNode!.insertBefore(ga, s);
  s.parentNode!.removeChild(ga);

  mandreelJsScriptLoaded();
}

function mandreelLoadCompressedJs(codeJs: string | null): void {}

function mandreelLoadMandreelJsScript(): void {
  if (mandreelAppMandreelJsCompressed) {
    mandreelFsLoadText(mandreelAppMandreelJs + '.lzma', mandreelLoadCompressedJs);
  } else {
    let ga = mandreelDocument.createElement('script');
    ga.type = 'text/javascript';
    ga.async = true;
    let url = mandreelAppMandreelJs;
    if (mandreelAppReadMandreelJsFromLocalHost) {
      ga.src = mandreelAppLocalHost + '/' + url;
    } else {
      ga.src = url;
    }
    let s = mandreelDocument.getElementsByTagName('script')[0];
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc('loadingScript', '');
    }
    s.parentNode!.insertBefore(ga, s);
  }
}

function mandreelFatLoaded(): void {
  if (commonVariable.mandreelAppPlatform === 'nacl') {
    commonVariable.gMandreelWorkingFolder = mandreelAppWorkingFolder;
    if (mandreelAppReadDataFromLocalHost) {
      commonVariable.gMandreelWorkingFolder = mandreelAppLocalHost + '/' + mandreelAppWorkingFolder;
    }
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc!('loadingAudio', '');
    }
    mandreelAppAudioReady();
  } else {
    mandreelLoadMandreelJsScript();
  }
}

function mandreelRemovecr(myString: string): string {
  let newString = '';
  return newString;
}

function mandreelAppInit(): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    globalInit(gStackPointer + numDefine800 * numDefine1024);
    globalInit1(gStackPointer + numDefine800 * numDefine1024);
    globalInit2(gStackPointer + numDefine800 * numDefine1024);
    globalInit3(gStackPointer + numDefine800 * numDefine1024);
    globalInit4(gStackPointer + numDefine800 * numDefine1024);
    globalInit5(gStackPointer + numDefine800 * numDefine1024);
    globalInit6(gStackPointer + numDefine800 * numDefine1024);
    globalInit7(gStackPointer + numDefine800 * numDefine1024);

    let sp: number = gStackPointer + numDefine800 * numDefine1024;
    heapClassInst.heapU32[sp >> numDefine2] = mandreelAppCanvasWidth;
    heapClassInst.heapU32[(sp + numDefine4) >> numDefine2] = mandreelAppCanvasHeight;
    mandreelInternalSetResolution(sp);
    mandreelInternalInit(gStackPointer + numDefine800 * numDefine1024);
    initNew(gStackPointer + numDefine800 * numDefine1024);
  }
}

function mandreelAppDraw(elapsed: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let sp: number = gStackPointer + numDefine800 * numDefine1024;
    mandreelInternalPreupdate(sp);
    heapClassInst.heapU32[sp >> numDefine2] = elapsed;
    draw(sp);
    mandreelInternalUpdate(sp);
    mandreelProcessAsyncCalls();
  }
}

function mandreelAppMouseWheel(delta: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = delta;
    mouseWheelDelta(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppMouseMove(x: number, y: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = x;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = y;
    mouseMove(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppResize(x: number, y: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = x;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = y;
    resize(i7);
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = x;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = y;
    mandreelInternalSetResolution(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppMouseButton(down: number, x: number, y: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = down;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = x;
    heapClassInst.heap32[(i7 + numDefine8) >> numDefine2] = y;
    mouseButton(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppMouseDblClick(x: number, y: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = x;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = y;
    mouseDoubleClick(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppKeyEvent(down: number, keyId: number): void {
  if (commonVariable.mandreelAppPlatform === 'webgl' || commonVariable.mandreelAppPlatform === 'canvas') {
    let i7 = mandreelLockFrame();
    heapClassInst.heap32[(i7 + 0) >> numDefine2] = down;
    heapClassInst.heap32[(i7 + numDefine4) >> numDefine2] = keyId;
    keyEvent(i7);
    mandreelUnlockFrame();
  }
}

function mandreelAppGetPlatform(): string {
  return commonVariable.mandreelAppPlatform;
}

function mandreelWheel(event: MouseEvent): void {
  imandreelOnMouseWheel(event);
  event.preventDefault();
  event.returnValue = false;
}

function render(): void {
  let nowTime = dateNow();
  if (!gMandreelFrameLocked) {
    gMandreelFrameInframe = true;
    if (!imandreelPauseGame) {
      mandreelAppDraw(nowTime - imandreelOldTime);
    }
    gMandreelFrameInframe = false;
  }
  imandreelOldTime = nowTime;
}

function imandreelOnMouseWheel(e: MouseEvent): void {
  if (!imandreelIsReady) {
    return;
  }
  if (imandreelPauseGame) {
    return;
  }

  mandreelAppMouseWheel(e.wheelDelta);
}

function imandreelOnKeyUp(e: MouseEvent): void {
  if (!imandreelIsReady) {
    return;
  }
  if (imandreelPauseGame) {
    return;
  }
}

function imandreelOnKeyDown(e: MouseEvent): void {
  if (e.which === numDefine8) {
    e.preventDefault();
  }
  if (!imandreelIsReady) {
    return;
  }
  if (imandreelPauseGame) {
    return;
  }
}

function mandreelInterSwfLoaded(): void {
  appStartState('scriptLoaded');
  let flashMovie: Param = swfObject.getObjectById(mandreelAppCanvasDiv);
  dump(flashMovie.width);
  flashMovie.mandreelInit();

  imandreelFlashGlobalSp = mandreelInterGetSWF.mandreelInterGetGlobalStack();
  imandreelFlashGlobalSp2 = mandreelInterGetSWF.mandreelInterGetGlobalStack2();
  if (mandreelAppStartStateFunc !== null) {
    mandreelAppStartStateFunc('ready', mandreelAppCanvasWidth, mandreelAppCanvasHeight);
  }
}

function mandreelInterCallFunctionRaw(sp: number, funcName: string, returnType: string): string {
  return mandreelInterGetSWF.mandreelInterCallFunctionRaw(sp, funcName, returnType);
}

function mandreelIsFilesystem(): boolean {
  if (mandreelIndexedDB.db !== null) {
    return false;
  }

  if (!variableSet.gMandreelFs !== null) {
    return false;
  }

  return true;
}

function mandreelIsIndexeddb(): boolean {
  if (mandreelIndexedDB.db !== null) {
    return true;
  }

  return false;
}

function mandreelFsSaveFile(name: string | null, myArrayBuffer: ArrayBuffer | number | string): void {}

function mandreelFsLoadText(fileName: string, callback: (v: string | null) => void): void {}

function mandreelAppFullscreen(enable: number): void {}

function mandreelAppDisableForceFocus(): void {
  mandreelAppForceFocus = false;
}

function mandreelAppEnableForceFocus(): void {
  mandreelAppForceFocus = true;
}

function atob(str: string): string {
  return '';
}

function imandreelAs3Dump(param: string): void {
  dump(atob(param));
}

function pluginMandreelInterWriteInt(ptr: number, value: number): void {
  mandreelEmbedPlugin.mandreelInterCalls('WriteInt', ptr, value);
}

function pluginMandreelInterWriteFloat(ptr: number, value: number): void {
  mandreelEmbedPlugin.mandreelInterCalls('WriteFloat', ptr, value);
}

function pluginMandreelInterWriteString(ptr: number, value: number): void {
  mandreelEmbedPlugin.mandreelInterCalls('WriteString', ptr, value);
}

function pluginMandreelInterWriteWString(ptr: number, value: number): void {
  mandreelEmbedPlugin.mandreelInterCalls('WriteWString', ptr, value);
}

function pluginMandreelPause(): void {
  mandreelEmbedPlugin.mandreelInterCalls('Pause');
}

function pluginMandreelResume(): void {
  mandreelEmbedPlugin.mandreelInterCalls('Resume');
}

function pluginMandreelUnlockFrame(): void {
  mandreelEmbedPlugin.mandreelInterCalls('UnlockFrame');

  mandreelPluginCurrentSp -= numDefine300 * numDefine1024;
}

function mandreelPluginDraw(): void {
  let canvas = mandreelDocument.getElementById('canvasDiv');
  mandreelEmbedPlugin.mandreelInterCalls('AppDraw');
}

function mandreelInitPluginPlatform(params: Param): void {}

function mandreelInterSwfProgress(mode: string, percentage: number, bytes: number, totalbytes: number): void {
  imandreelUpdateLoad(bytes, totalbytes);
  if (mode === 'files') {
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc('loadingData', percentage);
    }
  } else if (mode === 'audio') {
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc('loadingAudioUpdate', percentage);
    }
  } else if (mode === 'textureasync') {
    if (mandreelAppStartStateFunc !== null) {
      mandreelAppStartStateFunc('loadingTextureAsyncPack', percentage);
    }
  }
}

function myAssert(sp: number): void {
  let sp0 = sp;
  let p0 = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let p1 = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  assert(false, 'hola');
}

function writeHeapDouble(addr: number, value: number): void {
  heapClassInst.heapDouble[addr >> numDefine3] = value;
}

function writeHeapU64(addr: number, value: HeapValue): void {
  heapClassInst.heap32[addr >> numDefine2] = value.l;
  heapClassInst.heap32[(addr >> numDefine2) + 1] = value.h;
}

function z11printValued(stackPos: string, value: string): void {
  dump(value);
  dump('\n');
}

function z11printLabelPKc(stackPos: number, addr: string): void {
  puts(stackPos, addr);
  dump('\n');
}

function mallocSize(sp: number): void {
  let sp0 = sp;
  let ptr = heapClassInst.heapU32[sp0 >> numDefine2];
  sp0 -= numDefine4;

  heapClassInst.heap32[sp0 >> numDefine2] = ptr;
  tlsfBlockSize(sp0);
}

function tlsfBlockSize(sp: number): void {}

function llvmMoveFloat(addrDst: number, addrSrc: number): void {
  heapClassInst.heapU32![addrDst] = heapClassInst.heapU32[addrSrc];
}

function log10f(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.log(value) / mandeelLog10;
}

function log10(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.log(value) / mandeelLog10;
}

function logf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.log(value);
}

function log(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.log(value);
}

function asinf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.asin(value);
}

function asin(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.asin(value);
}

function acos(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.acos(value);
}

function floor(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.floor(value);
}

function floorf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.floor(value);
}

function round(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.round(value);
}

function roundf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.round(value);
}

function ceilf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.ceil(value);
}

function ceil(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.ceil(value);
}

function exp2(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];

  commonVariable.fg0 = Math.pow(numDefine2, value);
}

function exp2f(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];

  commonVariable.fg0 = Math.pow(numDefine2, value);
}

function pow(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  let value2: number = heapClassInst.heapDouble[(sp + numDefine8) >> numDefine3];
  commonVariable.fg0 = Math.pow(value, value2);
}

function cos(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.cos(value);
}

function tan(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.tan(value);
}

function expf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine3];
  commonVariable.fg0 = Math.exp(value);
}

function tanf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.tan(value);
}

function atanf(sp: number): void {
  let value: number = heapClassInst.heapFloat[sp >> numDefine2];
  commonVariable.fg0 = Math.atan(value);
}

function atan(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.atan(value);
}

function abs(sp: number): void {
  let value: number = heapClassInst.heap32[sp >> numDefine2];
  if (value < 0) {
    commonVariable.rg0 = -value;
  } else {
    commonVariable.rg0 = value;
  }
}

function sin(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.sin(value);
}

function sqrt(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.sqrt(value);
}

function fmod(sp: number): void {
  let sp0 = sp;
  let value: number = heapClassInst.heapDouble[sp0 >> numDefine3];
  sp0 += numDefine8;
  let value2: number = heapClassInst.heapDouble[sp0 >> numDefine3];
  commonVariable.fg0 = value % value2;
}

function atan2(sp: number): void {
  let x: number = heapClassInst.heapDouble[sp >> numDefine3];
  let y: number = heapClassInst.heapDouble[(sp + numDefine8) >> numDefine3];
  commonVariable.fg0 = Math.atan2(x, y);
}

function fabs(sp: number): void {
  let value: number = heapClassInst.heapDouble[sp >> numDefine3];
  commonVariable.fg0 = Math.abs(value);
}

function z18OutputDebugStringAPKc(sp: number): void {
  puts(sp);
}

function getenv(sp: number): void {
  commonVariable.rg0 = 0;
}

function mandreelFcmpOrd(x: number, y: number): boolean {
  return x === x && y === y;
}

function mandreelFcmpUno(x: number, y: number): boolean {
  return x !== x || y !== y;
}

function mandreelRewind(sp: number): void {
  let sp0 = sp;
  let fileId: number = heapClassInst.heap32[sp >> numDefine2];
  sp0 += numDefine4;

  fileIds[fileId]!.offset = 0;

  commonVariable.rg0 = 0;
}

function mandreelGetc(sp: number): void {
  let sp0 = sp;
  let fileId = heapClassInst.heap32[sp >> numDefine2];
  sp0 += numDefine4;
  let offset = fileIds[fileId]!.offset;
  let buffer = fileIds[fileId]!.buffer;

  let byteArray = fileIds[fileId]!.byteArray;

  let total = 1;

  let result = 0;

  if ((offset + total) > buffer.byteLength) {
    result = -1;
  } else {
    result = byteArray[offset];
    fileIds[fileId]!.offset += total;
  }
  commonVariable.rg0 = result;
}

function llvmStoreUnalign32Float(addr: string, value: number): void {
  heapClassInst.heapFloat[0] = value;
  let data = heapClassInst.heap32[0];
  heapClassInst.heap8[addr] = data & numDefine0xff;
  heapClassInst.heap8[addr + 1] = (data >>> numDefine8) & numDefine0xff;
  heapClassInst.heap8[addr + numDefine2] = (data >>> numDefine16) & numDefine0xff;
  heapClassInst.heap8[addr + numDefine3] = (data >>> numDefine24) & numDefine0xff;
}

function llvmStoreUnalign32(addr: string, value: number): void {
  heapClassInst.heap8[addr] = value & numDefine0xff;
  heapClassInst.heap8[addr + 1] = (value >>> numDefine8) & numDefine0xff;
  heapClassInst.heap8[addr + numDefine2] = (value >>> numDefine16) & numDefine0xff;
  heapClassInst.heap8[addr + numDefine3] = (value >>> numDefine24) & numDefine0xff;
}

function llvmReadUnalign32(addr: number): number {
  let value: number = 0;
  value = heapClassInst.heapU8[addr];
  value |= heapClassInst.heapU8[addr + 1] << numDefine8;
  value |= heapClassInst.heapU8[addr + numDefine2] << numDefine16;
  value |= heapClassInst.heapU8[addr + numDefine3] << numDefine24;
  return value;
}

function llvmReadUnalign32Float(addr: number): number {
  let value: number = 0;
  value = heapClassInst.heapU8[addr];
  value |= heapClassInst.heapU8[addr + 1] << numDefine8;
  value |= heapClassInst.heapU8[addr + numDefine2] << numDefine16;
  value |= heapClassInst.heapU8[addr + numDefine3] << numDefine24;

  heapClassInst.heap32[0] = value;
  return heapClassInst.heapFloat[0];
}

function mandreelRemovels(sp: number): void {
  let sp0 = sp;
  let ptrName = heapClassInst.heap32[sp >> numDefine2];
  sp0 += numDefine4;
  let key = getStringFromPtr(ptrName);

  let myLocalStorage = mandreelGetlocalstorage();

  myLocalStorage?.removeItem(key);
  myLocalStorage?.removeItem(key + 'size');
  commonVariable.rg0 = 0;
}

function mandreelRenamels(sp: number): void {
  let sp0 = sp;
  let ptrName0 = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let ptrName1 = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;

  let key0 = getStringFromPtr(ptrName0);
  let key1 = getStringFromPtr(ptrName1);

  let myLocalStorage = mandreelGetlocalstorage();

  let value = myLocalStorage?.getItem(key0);
  let value2 = myLocalStorage?.getItem(key1 + 'size');

  if (value !== undefined && value2 !== undefined && value !== null && value2 !== null) {
    myLocalStorage?.setItem(key1, value);
    myLocalStorage?.setItem(key1 + 'size', value2);

    myLocalStorage?.removeItem(key0);
    myLocalStorage?.removeItem(key0 + 'size');
    commonVariable.rg0 = 0;
  } else {
    commonVariable.rg0 = -1;
  }
}

function getStringFromWptr(ptr: number): string {
  ptr = ptr;
  let ret = '';
  if (ptr === 0) {
    return ret;
  }
  assert((ptr & 1) === 0);
  ptr >>= 1;
  let i = 0;
  while (true) {
    if (heapClassInst.heapU16[ptr + i] === 0) {
      break;
    }
    let t = String.fromCharCode(heapClassInst.heapU16[ptr + i]);

    ret += t;
    i += 1;
  }

  return ret;
}

function fillWptrFromString(ptr: number, v: string): void {
  ptr = ptr;
  assert((ptr & 1) === 0);
  ptr >>= 1;
  heapClassInst.heapU16[ptr] = 0;
}

function mandreelInterRetParam(type: string, value: number): number {
  switch (type) {
    case 'int':
      commonVariable.rg0 = value;
      break;
    case 'float':
      commonVariable.fg0 = value;
      break;
    default:
      assert(false);
      break;
  }
  return 0;
}

function mandreelInterGetFunctionPtr(value: number): (number: number) => void {
  return functionTable[value >> numDefine2];
}

function mandreelTextureAsyncCheckPending(sp: number): void {
  commonVariable.rg0 = mandreelTextureAsyncTextures - mandreelTextureAsyncTexturesLoaded;
}

function mandreelTextureAsyncGetProperties(sp: number): void {
  let sp0 = sp;
  let textureId = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let ptrWidth = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let ptrHeight = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;

  let tex = arrayIdsOgl[textureId];

  if (tex === null || tex.mandreelWidth === 0) {
    commonVariable.rg0 = 0;
  } else {
    heapClassInst.heap32[ptrWidth >> numDefine2] = tex.mandreelWidth;
    heapClassInst.heap32[ptrHeight >> numDefine2] = tex.mandreelHeight;
    commonVariable.rg0 = 1;
  }
}

function mandreelInterWriteString(ptr: number, value: string): void {
  fillPtrFromString(ptr, value);
}

function mandreelInterWriteWString(ptr: number, value: string): void {
  fillWptrFromString(ptr, value);
}

function mandreelInterWriteFloat(ptr: number, value: number): void {
  heapClassInst.heapFloat[ptr >> numDefine2] = value;
}

function mandreelPause(): void {
  imandreelPauseGame = true;
}

function mandreelResume(): void {
  imandreelPauseGame = false;
}

function mandreelLockFrame(): number {
  assert(gMandreelFrameInframe === false, 'calling lockframe during render frame');
  assert(gMandreelFrameLocked === false, 'calling lockframe twice');
  gMandreelFrameLocked = true;

  return gStackPointer + numDefine800 * numDefine1024;
}

function mandreelUnlockFrame(): void {
  assert(gMandreelFrameInframe === false);
  gMandreelFrameLocked = false;
}

function mandreelInterWriteInt(ptr: number, value: number): void {
  heapClassInst.heap32[ptr >> numDefine2] = value;
}

function mandreelInterStringFromWPtr(ptr: number): string {
  return getStringFromWptr(ptr);
}

function mandreelInterStringFromPtr(ptr: number): string {
  return getStringFromPtr(ptr);
}

function mandreelMyCallExternalArray(method: string, params: Array<number | string>): Array<number> {
  let result: number = 0;
  let resultString: number = 0;
  return [result, resultString];
}

function mandreelInterjsCall(sp: number): void {
  let sp0 = sp;
  let methodPtr = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let method = getStringFromPtr(methodPtr);
  let params: Array<number | string> = new Array<number | string>();
  params.push(sp0);

  let varInt: number = 0;
  let varString: string = '';
  let varDouble: number = 0.0;

  let returnType: number = 0;
  let returnPtr: number = 0;
  while (true) {
    let myType = heapClassInst.heap32[sp0 >> numDefine2];
    sp0 += numDefine4;
    if (myType === mandreelcalljsTypeReturnVoid) {
      returnType = myType;
      break;
    } else if (myType === mandreelcalljsTypeInt) {
      varInt = heapClassInst.heap32[sp0 >> numDefine2];
      params.push(varInt);
      sp0 += numDefine4;
    } else if (myType === mandreelcalljsTypeFloat) {
      sp0 = (sp0 + numDefine7) & ~numDefine7;
      varDouble = llvmReadDouble(sp0);
      params.push(varDouble);
      sp0 += numDefine8;
    } else if (myType === mandreelcalljsTypeString) {
      varInt = heapClassInst.heap32[sp0 >> numDefine2];
      varString = getStringFromPtr(varInt);
      params.push(varString);
      sp0 += numDefine4;
    } else if (myType === mandreelcalljsTypeReturnInt) {
      returnType = myType;
      returnPtr = heapClassInst.heap32[sp0 >> numDefine2];
      break;
    } else if (myType === mandreelcalljsTypeReturnFloat) {
      returnType = myType;
      returnPtr = heapClassInst.heap32[sp0 >> numDefine2];
      break;
    } else {
      assert(false, 'invalid arguments calling mandreelInterjsCall');
    }
  }

  let result = mandreelMyCallExternalArray(method, params);
  commonVariable.rg0 = result[0];

  if (commonVariable.rg0 === 0) {
    if (returnType === mandreelcalljsTypeReturnInt) {
      heapClassInst.heap32[returnPtr >> numDefine2] = result[1];
    } else if (returnType === mandreelcalljsTypeReturnFloat) {
      heapClassInst.heapFloat[returnPtr >> numDefine2] = result[1];
    }
  }
}

function mandreelInterJSAsyncCall(sp: number): void {
  let sp0 = sp;
  let methodPtr = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let funcName = getStringFromPtr(methodPtr);
  let paramPtr = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let param = getStringFromPtr(paramPtr);
  mandreelAsyncCallsJs.push(new FunClass(funcName, param));
}

function mandreelInterCallFunctionAsync(funcName: string, param: string): void {
  mandreelAsyncCallsMandreel.push(new FunClass(funcName, param));
}

function mandreelProcessAsyncCalls(): void {
  if (mandreelAsyncCallsMandreel.length > 0) {
    let tempList: Array<FunClass> = mandreelAsyncCallsMandreel.slice(0);

    mandreelAsyncCallsMandreel = [];

    for (let i = 0; i < tempList.length; ++i) {
      let param = tempList[i].param;
      let funcName = tempList[i].funcName;

      let size = (param.length + 1 + numDefine7) & numDefine0xfffffff8;

      let sp = gStackPointer + numDefine800 * numDefine1024;

      let strPtr = sp - size;
      fillPtrFromString(strPtr, param);

      sp = strPtr - numDefine4;
      heapClassInst.heap32[sp >> numDefine2] = strPtr;
    }
  }

  if (mandreelAsyncCallsJs.length > 0) {
    let tempList = mandreelAsyncCallsJs.slice(0);

    mandreelAsyncCallsJs = [];

    for (let i = 0; i < tempList.length; ++i) {
      let param = tempList[i].param;
      let funcName = tempList[i].funcName;
    }
  }
}

function mandreelInternalIsCanvas(sp: number): void {
  if (commonVariable.mandreelAppPlatform === 'canvas') {
    commonVariable.rg0 = 1;
  } else {
    commonVariable.rg0 = 0;
  }
}

function mandreelDeviceSetFullScreen(sp: number): void {
  let sp0 = sp;
  let enable = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  mandreelAppFullscreen(enable);
}

function myglCreateUniformArrays(): void {
  if (uniformArraysCreated === 0) {
    for (let i = 0; i < numDefine256; i++) {
      uniformArrays2[i] = new Float32Array(i * numDefine2).fill(0);
      uniformArrays3[i] = new Float32Array(i * numDefine3).fill(0);
      uniformArrays4[i] = new Float32Array(i * numDefine4).fill(0);
    }
    uniformArraysCreated = 1;
  }
}

function myglNewSlot(): number {
  let id = mySuperId;
  mySuperId++;
  return id;
}

function myglFreeSlot(id: number): void {
  arrayIdsOglEnable.push(id);
}

function myglCreateTexture(sp: number): void {
  let id: number = myglNewSlot();
  commonVariable.rg0 = id;
}

function myglCreateRenderBuffer(sp: number): void {
  let id: number = myglNewSlot();
  arrayIdsOgl[id] = imandreelGl!.createRenderbuffer();
  commonVariable.rg0 = id;
}

function myglCreateFrameBuffer(sp: number): void {
  let id = myglNewSlot();
  arrayIdsOgl[id] = imandreelGl!.createFramebuffer();
  commonVariable.rg0 = id;
}

function myglCreateBuffer(sp: number): void {
  let id = myglNewSlot();
  arrayIdsOgl[id] = imandreelGl!.createBuffer();
  commonVariable.rg0 = id;
}

function myglGetShaderInfoLog(sp: number): void {
  let sp0 = sp;
  let programId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let ptrString: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let infoLog: string = imandreelGl!.getShaderInfoLog(arrayIdsOgl[programId]);
  fillPtrFromString(ptrString, infoLog);
}

function myglGetError(sp: number): void {
  commonVariable.rg0 = imandreelGl!.getError();
  commonVariable.rg0 = 0;
}

function myglGetProgramParameter(sp: number): void {
  let sp0 = sp;
  let programId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let pname: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  commonVariable.rg0 = imandreelGl!.getProgramParameter(arrayIdsOgl[programId], pname);
}

function myglGetActiveAttrib(sp: number): void {
  let sp0 = sp;
  let programId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let index: number = heapClassInst.heap32[sp >> numDefine2];
  sp0 += numDefine4;
  let ptr: number = heapClassInst.heap32[sp >> numDefine2];
  sp0 += numDefine4;
  let result = imandreelGl!.getActiveAttrib(arrayIdsOgl[programId], index);
  if (result !== null) {
    heapClassInst.heap32[ptr >> numDefine2] = result.size;
    heapClassInst.heap32[(ptr + numDefine4) >> numDefine2] = result.type;
    fillPtrFromString(ptr + numDefine8, result.name);
    commonVariable.rg0 = 0;
  } else {
    commonVariable.rg0 = 1;
  }
}

function myglGetActiveUniform(sp: number): void {
  let sp0 = sp;
  let programId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let index: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let ptr: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  let result = imandreelGl!.getActiveUniform(arrayIdsOgl[programId], index);
  if (result !== null) {
    heapClassInst.heap32[ptr >> numDefine2] = result.size;
    heapClassInst.heap32[(ptr + numDefine4) >> numDefine2] = result.type;
    fillPtrFromString(ptr + numDefine8, result.name);
    commonVariable.rg0 = 0;
  } else {
    commonVariable.rg0 = 1;
  }
}

function mygetProgramParameter(sp: number): void {
  let sp0 = sp;
  let programId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let pname: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  commonVariable.rg0 = imandreelGl!.getProgramParameter(arrayIdsOgl[programId], pname);
}

function mygetShaderParameter(sp: number): void {
  let sp0 = sp;
  let shaderId: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 = sp0 + numDefine4;
  let pname: number = heapClassInst.heap32[sp0 >> numDefine2];
  sp0 += numDefine4;
  commonVariable.rg0 = imandreelGl!.getShaderParameter(arrayIdsOgl[shaderId], pname);
}

function myglInit(sp: number): void {
  for (let i = 0; i < maxOglId; ++i) {
    arrayIdsOglEnable.push(i + 1);
  }
}

export {
  latencyMandreel,
  mandreelSetTimeout,
  dump,
  mandreelCheckState,
  startApp,
  appStartState,
  mandreelSwfSizeLoaded,
  mandreelNextDecompress,
  mandreelDecompressJSReady,
  mandreelAppMouseMove,
  mandreelAppMouseButton,
  mandreelAppMouseDblClick,
  mandreelAppGetPlatform,
  Resxy,
  mandreelWheel,
  imandreelOnMouseWheel,
  imandreelOnKeyUp,
  imandreelOnKeyDown,
  mandreelInterSwfLoaded,
  mandreelInterWriteInt,
  mandreelInterWriteFloat,
  mandreelInterWriteString,
  mandreelInterWriteWString,
  mandreelInterCallFunctionRaw,
  mandreelResume,
  mandreelInterCallFunctionAsync,
  mandreelIsFilesystem,
  mandreelIsIndexeddb,
  mandreelAppFullscreen,
  mandreelAppDisableForceFocus,
  mandreelAppEnableForceFocus,
  imandreelAs3Dump,
  pluginMandreelInterWriteInt,
  pluginMandreelInterWriteFloat,
  pluginMandreelInterWriteString,
  pluginMandreelInterWriteWString,
  pluginMandreelPause,
  pluginMandreelResume,
  pluginMandreelUnlockFrame,
  mandreelPluginDraw,
  mandreelInterSwfProgress,
  myAssert,
  writeHeapDouble,
  writeHeapU64,
  z11printValued,
  z11printLabelPKc,
  mallocSize,
  llvmMoveFloat,
  log10f,
  log10,
  logf,
  log,
  asinf,
  asin,
  acos,
  floor,
  floorf,
  round,
  roundf,
  ceilf,
  ceil,
  exp2,
  exp2f,
  pow,
  cos,
  tan,
  expf,
  tanf,
  atanf,
  atan,
  abs,
  sin,
  sqrt,
  fmod,
  atan2,
  fabs,
  z18OutputDebugStringAPKc,
  getenv,
  mandreelFcmpOrd,
  mandreelFcmpUno,
  mandreelRewind,
  mandreelGetc,
  llvmStoreUnalign32Float,
  llvmStoreUnalign32,
  llvmReadUnalign32,
  llvmReadUnalign32Float,
  mandreelRemovels,
  mandreelRenamels,
  getStringFromWptr,
  fillWptrFromString,
  mandreelInterRetParam,
  mandreelInterGetFunctionPtr,
  mandreelTextureAsyncCheckPending,
  mandreelTextureAsyncGetProperties,
  mandreelInterStringFromWPtr,
  mandreelInterStringFromPtr,
  mandreelMyCallExternalArray,
  mandreelInterjsCall,
  mandreelInterJSAsyncCall,
  mandreelInternalIsCanvas,
  mandreelDeviceSetFullScreen,
  myglCreateUniformArrays,
  myglNewSlot,
  myglFreeSlot,
  myglCreateTexture,
  myglCreateRenderBuffer,
  myglCreateFrameBuffer,
  myglGetShaderInfoLog,
  myglGetError,
  myglGetProgramParameter,
  myglGetActiveAttrib,
  myglGetActiveUniform,
  mygetProgramParameter,
  mygetShaderParameter,
  myglInit,
  fillPtrFromString,
  getStringFromPtr
};

export { setupMandreel, runMandreel };
